#include "MyCircle.h"
#include "ScreenApp.h"

//copy c'tor
MyCircle::MyCircle(MyCircle &other)
{
	m_radius = other.getRadius();
}

//c'tor with file
MyCircle::MyCircle( ifstream &in_file, bool binary): Shape(in_file,binary)
{
	if(binary)
		in_file.read( (char*)&m_radius, sizeof(int) );
	else
		in_file >> m_radius;
		//in_file>>*this;
}

void MyCircle::setShape()
{
	cout << "Enter the Circle's Center Coordinates [x y]:";
	Shape::setPoint();
	cout << "Enter Circle's Radius: ";
	cin >> m_radius;
}

void MyCircle::draw(char sign)
{
	int f = 1 - m_radius;
	int ddF_x = 1;
	int ddF_y = -2 * m_radius;
	int x = 0;
	int y = m_radius;
	int x0 = m_point.getX();
	int y0 = m_point.getY();
	
	gotoxy(x0, y0 + m_radius);
	cout << sign;
	gotoxy(x0, y0 - m_radius);
	cout << sign;
	gotoxy(x0 + m_radius, y0);
	cout << sign;
	gotoxy(x0 - m_radius, y0);
	cout << sign;
	
	while( (x+1) < y)
	{
		if(f >= 0) 
		{
			y--;
			ddF_y += 2;
			f += ddF_y;
		}
		x++;
		ddF_x += 2;
		f += ddF_x;    

		gotoxy(x0 + x, y0 + y);
		cout << sign;
		gotoxy(x0 - x, y0 + y);
		cout << sign;
		gotoxy(x0 + x, y0 - y);
		cout << sign;
		gotoxy(x0 - x, y0 - y);
		cout << sign;
		gotoxy(x0 + y, y0 + x);
		cout << sign;
		gotoxy(x0 - y, y0 + x);
		cout << sign;
		gotoxy(x0 + y, y0 - x);
		cout << sign;
		gotoxy(x0 - y, y0 - x);
		cout << sign;
	}
}

void MyCircle::drawWithHiding(char sign)
{
	int f = 1 - m_radius;
	int ddF_x = 1;
	int ddF_y = -2 * m_radius;
	int x = 0;
	int y = m_radius;
	int center_x = m_point.getX();
	int center_y = m_point.getY();
	int end;
	
	for(int i = center_x - m_radius + 1; i <= center_x + m_radius - 1; i++)
	{
		gotoxy(i,center_y);
		cout << " ";
	}

	for(int i = 0 ; i < (m_radius-1); i++ )
	{
		if(f >= 0) 
		{
			y--;
			ddF_y += 2;
			f += ddF_y;
		}

		x++;
		ddF_x += 2;
		f += ddF_x;

		end = y * 2 - 2;
		for( int i = 0; i <= end; ++i)
		{
			gotoxy(center_x - y + 1 + i, center_y + x);
			cout << " ";
			gotoxy(center_x - y + 1 + i, center_y - x);
			cout << " ";
		}
	}
	draw(sign);
}

bool MyCircle::isInShape(int x,int y)
{
	bool in_circle = true;
	if( (int)(sqrt( ( pow( (double)(x - getX()) ,2) ) + ( pow( (double)(y - getY()),2 )))) <= m_radius)
		return in_circle;

	return !in_circle;
}

Shape* MyCircle::copyShape(const Shape* other)
{
	MyCircle *tmp = new MyCircle;
	*tmp = *((MyCircle*)other);
	return tmp;
}

void MyCircle::drawToBuffer(screen &scr)
{
	int l_cx = getX();
	int l_cy = getY();
	int l_radius = m_radius;
	int x = 0;
	int y = l_radius;
	bool once = true;
	int f = 1 - l_radius;
	int ddF_x = 1;
	int ddF_y = -2 * l_radius;

	scr.changeCell(l_cx, l_cy + l_radius, '*');
	scr.changeCell(l_cx,l_cy - l_radius, '*');
	scr.changeCell(l_cx + l_radius,l_cy, '*');
	scr.changeCell(l_cx - l_radius,l_cy, '*');
	
	while( (x+1) < y)
	{
		if(f >= 0) 
		{
			y--;
			ddF_y += 2;
			f += ddF_y;
		}
		x++;
		ddF_x += 2;
		f += ddF_x;    

		scr.changeCell(l_cx + x,l_cy + y, '*');
		scr.changeCell(l_cx - x,l_cy + y, '*');
		scr.changeCell(l_cx + x,l_cy - y, '*');
		scr.changeCell(l_cx - x,l_cy - y, '*');
		scr.changeCell(l_cx + y,l_cy + x, '*');
		scr.changeCell(l_cx - y,l_cy + x, '*');
		scr.changeCell(l_cx + y,l_cy - x, '*');
		scr.changeCell(l_cx - y,l_cy - x, '*');
	}
}

//end of screen check
void MyCircle::calcShapeMove(ShapeContainer &work_cont, list<Shape*>::iterator itr, bool withTransfer) //checks screen collision
{
	double x_shift = getShiftX();
	double y_shift = getShiftY();

	if(withTransfer)
	{
		//end of screen check
		//width
		if(x_shift > 0 && (getX() + m_radius) >= (s_width-1))
			setX( m_radius - 1);
		else if(x_shift < 0 && (getX() - m_radius) <= 0)
			setX( s_width - m_radius);
		//height
		if( (y_shift > 0) &&( getY() + m_radius >= (s_height-1)) )
			setY( m_radius - 1);
		else if( (y_shift < 0) &&( ( getY() - m_radius) <= 0))
			setY( s_height - m_radius);
	}
	//with collision
	else
	{
		//end of screen check
		//width
		//left
		if( x_shift < 0 && getX() - m_radius <= 0 )
		{
			setShiftX( getShiftX() * -1 );
			setShiftXTmp( getShiftXTmp() * -1 );
		}
		//right
		else if( x_shift > 0 && getX() + m_radius >= (s_width - 1) )
		{
			setShiftX( getShiftX() * -1 );
			setShiftXTmp( getShiftXTmp() * -1 );
		}
		//height
		//up
		if( y_shift < 0 && ( getY() - m_radius) <= 0 )
		{
			setShiftY( getShiftY() * -1 );
			setShiftYTmp( getShiftYTmp() * -1 );
		}
		//down
		else if( y_shift > 0 && getY() + m_radius >= (s_height- 1))
		{
			setShiftY( getShiftY() * -1 );
			setShiftYTmp( getShiftYTmp() * -1 );
		}
	}
	calcMoveAux(work_cont,itr,withTransfer);
}

//checks collision between shapes
void MyCircle::calcMoveAux(ShapeContainer &work_cont,list<Shape*>::iterator itr,bool withTransfer) 
{
	double tmp;
	//x
	if((*itr)->getShiftX() != 0)
	{
		ScreenApp scr_app;
		if(!withTransfer) //with collision
		{ 
			scr_app.circleCollisionAux(itr,work_cont);
			(*itr)->changeX((int)(*itr)->getShiftXTmp());
		}
		else //with transfer
			(*itr)->changeX((int)(*itr)->getShiftXTmp());

		scr_app.clearNewScreen();
		scr_app.updateNewScreen(work_cont);
		
		if( (*itr)->getShiftXTmp() < 0)
			(*itr)->setShiftXTmp((*itr)->getShiftX() + 1);
		else
			(*itr)->setShiftXTmp((*itr)->getShiftX() - 1);	
	}
	if((*itr)->getShiftY() != 0 )
	{
		//y
		(*itr)->setShiftYTmp(tmp = ((*itr)->getShiftYTmp() + (*itr)->getShiftY()) );
		if( abs(tmp) >= 1 )
		{
			ScreenApp scr_app;
			if(!withTransfer)
			{ 
				scr_app.circleCollisionAux(itr,work_cont);
				(*itr)->changeY((int)tmp);
			}
			else
				(*itr)->changeY((int)tmp);

			scr_app.clearNewScreen();
			scr_app.updateNewScreen(work_cont);

			if( tmp < 0)
				(*itr)->setShiftYTmp((*itr)->getShiftY() + 1);
			else
				(*itr)->setShiftYTmp((*itr)->getShiftY() - 1);
		}
		else
			(*itr)->setShiftYTmp((*itr)->getShiftY() + (*itr)->getShiftYTmp() );
	}
}

void MyCircle::save( ofstream &out_file, bool binary)
{
	Shape::save(out_file, binary);
	if(binary)
		out_file.write( (const char*)&m_radius, sizeof(int) );
	else
		out_file << m_radius << endl;
}